#include <iostream>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <pthread.h>

#define NUM 5  // 创建五个子线程

// __thread关键字修饰，_thread修饰后的变量，每一个线程有一份独立实体，各个线程的值互不干扰。
// 可以用来修饰那些带有全局性且值可能变，但是又不值得用全局变量保护的变量。
// 只能修饰内置类型且程序地址空间中存储在全局区/静态区的变量
__thread int g_val = 100;
__thread pid_t pid = 0;

struct threadData
{
    std::string threadName;
};

std::string toHex(pthread_t tid)
{
    char buffer[128];
    snprintf(buffer, sizeof(buffer), "0x%x", tid);
    return buffer;
}

void InitThreadData(threadData *td, int number)
{
    td->threadName = "thread-" + std::to_string(number); // thread-0
}

void *threadRoutine(void *args)
{
    // pthread_detach，线程结束后会自行回收，无需pthread_join；
    // 可在其他线程设置，pthread_detach(pthread_t id)即可; 例如line 72-75
    // 本程序以主线程设置pthread_detach(pthread_t id)为例
    // pthread_detach(pthread_self());  
    threadData *td = static_cast<threadData*>(args);

    pid = getpid();
    std::string tid = toHex(pthread_self());

    int cnt = 5;
    while (cnt--)
    {
        std::cout <<"pthreadName: " << td->threadName << ", tid: " << tid 
        << ", pid: " << pid << ", g_val: " << g_val 
        << ", &g_val: " << &g_val << std::endl;
        g_val++;
        sleep(1);
    }
    
    delete td;
    return nullptr;
}

int main()
{
    std::vector<pthread_t> tids;
    for (int i = 1; i <= NUM; ++i)
    {
        pthread_t tid;
        // threadData* 在子线程内delete, 但是要注意不要越界访问
        threadData* td = new threadData;  

        InitThreadData(td, i);

        pthread_create(&tid, nullptr, threadRoutine, td);
        tids.push_back(tid);
        usleep(100000);
    }

    for (pthread_t itid : tids)
    {
        pthread_detach(itid);  // pthread_detach，线程结束后会自行回收，无需pthread_join；
    }

    // 由于设置了pthread_detach自动回收线程,因此pthread_join等待线程会失败
    for (int i = 0; i < tids.size(); i++)
    {
        int n = pthread_join(tids[i], nullptr);
        printf("n = %d, who = 0x%x, why: %s\n", n, tids[i], strerror(n));
    }
    
    // 此处需要设置sleep(5)等待子线程
    // 因为主线程结束后，子线程会跟着一起退出(pthread_exit主线程不会)，观察不到现象
    // 所以一般主线程是死循环运行的时候才会设置子线程pthread_detach
    sleep(5);

    return 0;
}